using System;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using Microsoft.Extensions.Logging;

namespace PrimeCalculator
{
    public enum AgentType
    {
        CONSUMER,
        PRODUCER,
        INVESTOR,
        GOVERNMENT
    }

    public static class NativeMethods
    {
        [DllImport("libAgentCore.so")]
        public static extern IntPtr CreateEnhancedAgents(int count, AgentType type);
        
        [DllImport("libAgentCore.so")]
        public static extern void RunEnhancedSimulation(int cycles, IntPtr consumers, int consumerCount,
                                                    IntPtr producers, int producerCount,
                                                    ref MarketConditions market);
                                                    
        [DllImport("libAgentCore.so")]
        public static extern void FreeEnhancedAgents(IntPtr agents);
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct MarketConditions
    {
        public double priceLevel;
        public double inflationRate;
        public double interestRate;
        public double unemploymentRate;
        public double tradeBalance;
    }

    public class SimulationParameters 
    {
        public int ConsumerCount { get; set; }
        public int ProducerCount { get; set; }
        public int SimulationCycles { get; set; }
    }

    public class SimulationResults 
    {
        public MarketConditions MarketData { get; set; }
        public int ConsumerCount { get; set; }
        public int ProducerCount { get; set; }
    }

    public static class Program
    {
        public static async Task Main(string[] args)
        {
            using var loggerFactory = LoggerFactory.Create(builder =>
            {
                builder.AddConsole();
                builder.SetMinimumLevel(LogLevel.Information);
            });
            
            var logger = loggerFactory.CreateLogger<Program>();

            try 
            {
                logger.LogInformation("=== Starting Economic Simulation ===");
                Console.WriteLine("=== Economic Simulation ===");
                
                await RunSimulationAsync(logger);
                
                logger.LogInformation("Simulation completed successfully");
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "Fatal error occurred");
                Console.Error.WriteLine($"Fatal error: {ex.Message}");
                Environment.ExitCode = 1;
            }
            finally 
            {
                logger.LogInformation("Simulation completed");
                Console.WriteLine("\nSimulation completed. Press any key to exit...");
                Console.ReadKey();
            }
        }

        public static async Task RunSimulationAsync(ILogger logger)
        {
            try 
            {
                var parameters = GetSimulationParameters();
                ValidateParameters(parameters);
                var results = await ExecuteSimulationAsync(parameters);
                PresentResults(results);
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"Simulation error: {ex.Message}");
                throw;
            }
        }

        public static SimulationParameters GetSimulationParameters()
        {
            return new SimulationParameters {
                ConsumerCount = GetValidInput("Enter consumer count (1-1000): ", 1, 1000),
                ProducerCount = GetValidInput("Enter producer count (1-500): ", 1, 500),
                SimulationCycles = GetValidInput("Enter cycles (1-100): ", 1, 100)
            };
        }

        public static int GetValidInput(string prompt, int min, int max)
        {
            while (true)
            {
                Console.Write(prompt);
                if (int.TryParse(Console.ReadLine(), out int value) && 
                    value >= min && value <= max)
                {
                    return value;
                }
                Console.WriteLine($"Please enter a number between {min}-{max}");
            }
        }

        public static void ValidateParameters(SimulationParameters parameters)
        {
            if (parameters == null)
                throw new ArgumentNullException(nameof(parameters));
            
            if (parameters.ConsumerCount < 1 || parameters.ConsumerCount > 1000)
                throw new ArgumentOutOfRangeException(nameof(parameters.ConsumerCount));
            
            if (parameters.ProducerCount < 1 || parameters.ProducerCount > 500)
                throw new ArgumentOutOfRangeException(nameof(parameters.ProducerCount));
            
            if (parameters.SimulationCycles < 1 || parameters.SimulationCycles > 100)
                throw new ArgumentOutOfRangeException(nameof(parameters.SimulationCycles));
        }

        public static async Task<SimulationResults> ExecuteSimulationAsync(SimulationParameters parameters, ILogger logger)
        {
            IntPtr consumers = IntPtr.Zero;
            IntPtr producers = IntPtr.Zero;
            
            try 
            {
                logger.LogDebug("Creating {ConsumerCount} consumer agents", parameters.ConsumerCount);
                consumers = NativeMethods.CreateEnhancedAgents(
                    parameters.ConsumerCount, AgentType.CONSUMER);
                
                logger.LogDebug("Creating {ProducerCount} producer agents", parameters.ProducerCount);
                producers = NativeMethods.CreateEnhancedAgents(
                    parameters.ProducerCount, AgentType.PRODUCER);

                MarketConditions market = new MarketConditions {
                    priceLevel = 1.0,
                    inflationRate = 0.02,
                    interestRate = 0.03,
                    unemploymentRate = 0.05,
                    tradeBalance = 0
                };

                logger.LogInformation("Starting simulation with {Cycles} cycles", parameters.SimulationCycles);
                NativeMethods.RunEnhancedSimulation(
                    parameters.SimulationCycles,
                    consumers,
                    parameters.ConsumerCount,
                    producers,
                    parameters.ProducerCount,
                    ref market);

                logger.LogInformation("Simulation completed successfully");
                logger.LogDebug("Final market conditions - Price: {Price}, Inflation: {Inflation}",
                    market.priceLevel, market.inflationRate);

                return new SimulationResults {
                    MarketData = market,
                    ConsumerCount = parameters.ConsumerCount,
                    ProducerCount = parameters.ProducerCount
                };
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "Error during simulation execution");
                throw;
            }
            finally 
            {
                if (consumers != IntPtr.Zero)
                {
                    logger.LogDebug("Releasing consumer agents");
                    NativeMethods.FreeEnhancedAgents(consumers);
                }
                
                if (producers != IntPtr.Zero)
                {
                    logger.LogDebug("Releasing producer agents");
                    NativeMethods.FreeEnhancedAgents(producers);
                }
            }
        }

        public static void PresentResults(SimulationResults results)
        {
            Console.WriteLine("\n=== SIMULATION RESULTS ===");
            Console.WriteLine($"Consumers: {results.ConsumerCount}");
            Console.WriteLine($"Producers: {results.ProducerCount}");
            Console.WriteLine($"Price Level: {results.MarketData.priceLevel:F2}");
            Console.WriteLine($"Inflation: {results.MarketData.inflationRate:P2}");
            Console.WriteLine($"Interest Rate: {results.MarketData.interestRate:P2}");
            Console.WriteLine($"Unemployment: {results.MarketData.unemploymentRate:P2}");
            Console.WriteLine($"Trade Balance: {results.MarketData.tradeBalance:C0}");
        }
    }
}